WebWorkTargetSource Shopping Cart Example

Here is a modified version of the shopping cart example which uses the WebWorkTargetSource. Its a quick hack to show how the WebWorkTargetSource and not as a complete solution or template for usage. If my documentation is unclear (probable) or none of this makes sense (quite possible too) then just replace the existing versions of DefaultCart.java and applicationContext.xml with these versions and fire up the example.

DefaultCart Modifications

Two modifications to DefaultCart.java are necessary in order to make the autowiring work. When spring goes to look for beans to autowire it will see two ShoppingCarts and barf as for autowiring to work it needs to see only one. To avoid this the DefaultCart has been modified to not implement the ShoppingCart interface. However there is a fun inner class and inner interface that makes this change a bit more complex. In order to make the DefaultCart compile (and still work) all references to CartEntry need to be changed to ShoppingCart.CartEntry.

DefaultCart.java
package com.opensymphony.webwork.example.ajax.cart;

import com.opensymphony.webwork.example.ajax.catalog.Product;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import sun.reflect.Reflection;

/**
 * DefaultCart - Poorly Modified by Eric Molitor <[email protected]>
 *
 * @author Jason Carreira <[email protected]>
 */
public class DefaultCart {
    Map contents = new HashMap();

    public static DefaultCart getCart() {
        return new DefaultCart();
    }

    public void addToCart(int quantity, Product product) {
        ShoppingCart.CartEntry entry = (ShoppingCart.CartEntry) contents.get(product);
        if (entry == null) {
            entry = new DefaultCartEntry(quantity, product);
            contents.put(product, entry);
        } else {
            entry.setQuantity(entry.getQuantity() + quantity);
        }
    }

    public void setQuantity(int quantity, Product product) {
        if (quantity <= 0) {
            contents.remove(product);
            return;
        }
        ShoppingCart.CartEntry entry = (ShoppingCart.CartEntry ) contents.get(product);
        if (entry == null) {
            entry = new DefaultCartEntry(quantity, product);
            contents.put(product, entry);
        } else {
            entry.setQuantity(quantity+entry.getQuantity());
        }
    }

    public void removeFromCart(Product product) {
        contents.remove(product);
    }

    public Set getContents() {
        return new HashSet(contents.values());
    }

    public int getQuantityForProduct(Product product) {
        ShoppingCart.CartEntry entry = (ShoppingCart.CartEntry) contents.get(product);
        if (entry == null) {
            return 0;
        }
        return entry.getQuantity();
    }

    public String toString() {
        return "DefaultCart{" +
                "contents=" + contents +
                "}";
    }

    public static Object getBean() {

       System.out.println("!!!!!!!!!!!! Parent is:" + Reflection.getCallerClass(1));
        new Exception("poo").printStackTrace();
       return new DefaultCart();
    }

    class DefaultCartEntry implements ShoppingCart.CartEntry {
        private int quantity;
        private Product product;

        public DefaultCartEntry(int quantity, Product product) {
            this.quantity = quantity;
            this.product = product;
        }

        public int getQuantity() {
            return quantity;
        }

        public void setQuantity(int quantity) {
            this.quantity = quantity;
        }

        public Product getProduct() {
            return product;
        }
    }
}

applicationContext.xml Modifications

In order to get a session specific shopping cart we need to modify the actionContext to call our WebWorkTargetSource. We do this by using a ProxyFactory which creates an object based on our interface (ShoppingCart) and uses the targetSource to invoke our custom TargetSource (WebWorkTargetSource). WebWorkTargetSource however needs to know the underlying implementation in order to fetch and create new instances. We pass a reference to the new shoppingCartTarget bean definition which just references our new DefaultCart. In order to keep things from getting confused we're set both beans to autowire by name.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans default-autowire="autodetect">

    <bean id="catalog"
          singleton="true"
          class="com.opensymphony.webwork.example.ajax.catalog.TestCatalog"/>

    <bean id="shoppingCart" class="org.springframework.aop.framework.ProxyFactoryBean" autowire="byName">

        <property name="proxyTargetClass">
            <value>true</value>
        </property>
        <property name="proxyInterfaces">
            <list>
                <value>com.opensymphony.webwork.example.ajax.cart.ShoppingCart</value>
            </list>
        </property>
        <property name="targetSource">
            <bean class="org.tuxbot.webwork.spring.WebWorkTargetSource">
                <property name="targetBeanName">
                    <value>shoppingCartTarget</value>
                </property>
            </bean>
        </property>
    </bean>

    <bean id="shoppingCartTarget"
          class="com.opensymphony.webwork.example.ajax.cart.DefaultCart"
          singleton="false" autowire="byName">
    </bean>

</beans>